package com.xiaoshuidi.cloud.module.bpm.service.reserveticket;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaoshuidi.cloud.framework.common.exception.ErrorCode;
import com.xiaoshuidi.cloud.framework.common.exception.ServiceException;
import com.xiaoshuidi.cloud.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.framework.datapermission.core.rule.dept.DeptDataPermissionRule;
import com.xiaoshuidi.cloud.framework.redis.lock.DistributeLock;
import com.xiaoshuidi.cloud.framework.security.core.LoginUser;
import com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils;
import com.xiaoshuidi.cloud.module.bpm.controller.admin.assignrecord.vo.TicketAssignRecordCreateReqVO;
import com.xiaoshuidi.cloud.module.bpm.controller.admin.assignrecord.vo.TicketAssignRecordRespVO;
import com.xiaoshuidi.cloud.module.bpm.controller.admin.reserveticket.vo.*;
import com.xiaoshuidi.cloud.module.bpm.controller.app.reserveticket.vo.MyReserveTicketPageRespVO;
import com.xiaoshuidi.cloud.module.bpm.controller.app.reserveticket.vo.ReserveCreateReqVO;
import com.xiaoshuidi.cloud.module.bpm.convert.assignrecord.TicketAssignRecordConvert;
import com.xiaoshuidi.cloud.module.bpm.convert.reserveticket.ReserveTicketConvert;
import com.xiaoshuidi.cloud.module.bpm.dal.dataobject.assignrecord.TicketAssignRecordDO;
import com.xiaoshuidi.cloud.module.bpm.dal.dataobject.reserveapartmentconfig.ReserveApartmentConfig;
import com.xiaoshuidi.cloud.module.bpm.dal.dataobject.reserveticket.ReserveTicketDO;
import com.xiaoshuidi.cloud.module.bpm.dal.mysql.reserveticket.ReserveTicketMapper;
import com.xiaoshuidi.cloud.module.bpm.enums.CustomerTypeEnum;
import com.xiaoshuidi.cloud.module.bpm.enums.DistributeLockConstants;
import com.xiaoshuidi.cloud.module.bpm.enums.reserveticket.ReserveProgressEnum;
import com.xiaoshuidi.cloud.module.bpm.enums.reserveticket.ReserveStatusEnum;
import com.xiaoshuidi.cloud.module.bpm.enums.reserveticket.UrgencyLevelEnum;
import com.xiaoshuidi.cloud.module.bpm.service.assignrecord.TicketAssignRecordService;
import com.xiaoshuidi.cloud.module.bpm.service.memo.BpmMemoService;
import com.xiaoshuidi.cloud.module.bpm.service.message.BpmMessageService;
import com.xiaoshuidi.cloud.module.bpm.service.message.dto.MessageWhenCreateReserveTicketReqDTO;
import com.xiaoshuidi.cloud.module.bpm.service.message.dto.MessageWhenReserveTicketReqDTO;
import com.xiaoshuidi.cloud.module.bpm.service.reserveapartmentconfig.ReserveApartmentConfigService;
import com.xiaoshuidi.cloud.module.bpm.service.swo.SwoTicketService;
import com.xiaoshuidi.cloud.module.bpm.service.task.BpmTaskService;
import com.xiaoshuidi.cloud.module.contract.api.ContractApplyApi;
import com.xiaoshuidi.cloud.module.finance.api.finance.FinancePaymentChangeLogApi;
import com.xiaoshuidi.cloud.module.member.api.feedback.FeedbackApi;
import com.xiaoshuidi.cloud.module.member.api.user.MemberUserApi;
import com.xiaoshuidi.cloud.module.member.api.user.dto.MemberUserRespDTO;
import com.xiaoshuidi.cloud.module.rooms.api.apartment.ApartmentApi;
import com.xiaoshuidi.cloud.module.rooms.api.apartment.dto.ApartmentListDto;
import com.xiaoshuidi.cloud.module.rooms.api.roomentrust.RoomEntrustApi;
import com.xiaoshuidi.cloud.module.rooms.api.roomhouse.RoomHouseApi;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.RoomStateRecordApi;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.vo.GetCurrentRecordRespVO;
import com.xiaoshuidi.cloud.module.rooms.enums.RentStatusEnum;
import com.xiaoshuidi.cloud.module.rooms.vo.RoomHouseRespVo;
import com.xiaoshuidi.cloud.module.system.api.permission.dto.DeptDataPermissionRespDTO;
import com.xiaoshuidi.cloud.module.system.api.user.AdminUserApi;
import com.xiaoshuidi.cloud.module.system.api.user.dto.AdminUserRespDTO;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.module.bpm.enums.ErrorCodeConstants.*;

/**
 * 预约工单 Service 实现类
 *
 * @author 合房租赁
 */
@Slf4j
@Service
@Validated
public class ReserveTicketServiceImpl implements ReserveTicketService {
    @Resource
    private ReserveTicketMapper reserveTicketMapper;
    @Resource
    private TicketAssignRecordService ticketAssignRecordService;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private BpmMessageService bpmMessageService;
    @Resource
    private MemberUserApi memberUserApi;
    @Resource
    private SwoTicketService swoTicketService;
    @Resource
    private RoomEntrustApi roomEntrustApi;
    @Resource
    private BpmTaskService bpmTaskService;
    @Resource
    private ApartmentApi apartmentApi;
    @Resource
    private RoomStateRecordApi roomStateRecordApi;
    @Resource
    private RoomHouseApi roomHouseApi;
    @Resource
    private ContractApplyApi contractApplyApi;

    @Resource
    private FinancePaymentChangeLogApi financePaymentChangeLogApi;
    @Resource
    private ReserveApartmentConfigService reserveApartmentConfigService;
    @Resource
    private FeedbackApi feedbackApi;
    @Resource
    private BpmMemoService bpmMemoService;

    /**
     * 创建预约工单
     *
     * @param userId
     * @param createReqVO 创建信息
     * @param from        1来自PC端 2来自租客端
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createReserveTicket(Long userId, ReserveTicketCreateReqVO createReqVO, Integer from) {
        try(DistributeLock lock = DistributeLock.getLock(DistributeLockConstants.RESERVE_LOCK)) {
            lock.lock();
            log.info("创建预约工单请求 - userId:{}", userId);

            ApartmentListDto apartmentListDto = apartmentApi.getById(createReqVO.getApartmentId()).getCheckedData();
            if (ObjectUtil.isEmpty(apartmentListDto)) {
                throw new ServiceException(APARTMENT_NOT_EXIST);
            }
            // 集中式门店需要检查预约人数配置
            if (apartmentListDto.getIsConcentrated()) {
                checkReserveNum(createReqVO.getApartmentId(), createReqVO.getReserveDate(), createReqVO.getReserveTimeStart(), createReqVO.getReserveTimeEnd(), createReqVO.getTimePeriod());
            }
            // 集中式门店, 房型必选
            if (apartmentListDto.getIsConcentrated() && ObjectUtil.isEmpty(createReqVO.getHouseType())) {
                throw new ServiceException(HOUSETYPE_NOT_NULL);
            }
            if (!apartmentListDto.getIsConcentrated() && ObjectUtil.isEmpty(createReqVO.getRoomId())) {
                throw new ServiceException(ROOM_HOUSE_NOT_NULL);
            }
            // 检查房源是否为空房
            if (ObjectUtil.isNotEmpty(createReqVO.getRoomId())) {
                GetCurrentRecordRespVO currentRecordRespVO = roomStateRecordApi.getCurrentRecord(createReqVO.getRoomId()).getCheckedData();
                log.info("当前房态信息：{}", JsonUtils.toJsonString(currentRecordRespVO));
                if (!RentStatusEnum.EMPTY.getValue().equals(currentRecordRespVO.getState()) || currentRecordRespVO.getIsLock()) {
                    throw new ServiceException(ROOM_HOUSE_STATE_ERROR);
                }
            }

            // 检查客户是否重复预约
            LambdaQueryWrapper<ReserveTicketDO> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(ReserveTicketDO::getMobile, createReqVO.getMobile());
            wrapper.eq(ReserveTicketDO::getApartmentId, createReqVO.getApartmentId());
            wrapper.in(ReserveTicketDO::getStatus, CollectionUtil.toList(ReserveStatusEnum.TO_BE_ASSIGNED.getValue(), ReserveStatusEnum.FOLLOW_UP.getValue()));
            List<ReserveTicketDO> reserveTicketDOS = reserveTicketMapper.selectList(wrapper);
            if (CollectionUtil.isNotEmpty(reserveTicketDOS)) {
                String msg = "您已预约过{}门店，无法重复预约，如需更改日期请先取消之前预约。";
                throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(), StrUtil.format(msg, apartmentListDto.getName()));
            }
            // 插入
            ReserveTicketDO reserveTicket = ReserveTicketConvert.INSTANCE.convert(createReqVO);
            reserveTicket.setUserId(userId);
            reserveTicket.setDeptId(apartmentListDto.getDeptId());
            reserveTicket.setProgress(ReserveProgressEnum.PENDING.getValue());
            reserveTicket.setReserveTimeIndex(createReqVO.getTimePeriod());
            // 如果是集中式, 取门店地址, 如果是分散式房源, 取房源地址
            if (apartmentListDto.getIsConcentrated()) {
                reserveTicket.setAddress(apartmentListDto.getCity() + apartmentListDto.getDistrict() + apartmentListDto.getStreet() + apartmentListDto.getAddress());
            } else {
                RoomHouseRespVo roomHouseRespVo = roomHouseApi.getRoomHouseinfoById(createReqVO.getRoomId()).getCheckedData();
                reserveTicket.setRoomFullName(roomHouseRespVo.getFullRoomName());
                reserveTicket.setAddress(roomHouseRespVo.getCity() + roomHouseRespVo.getDistrict() + roomHouseRespVo.getStreet() + roomHouseRespVo.getAddress() + roomHouseRespVo.getArea());
            }
            if (ObjectUtil.isEmpty(createReqVO.getFollowUpUserId())) {
                reserveTicket.setStatus(ReserveStatusEnum.TO_BE_ASSIGNED.getValue());
            } else {
                reserveTicket.setStatus(ReserveStatusEnum.FOLLOW_UP.getValue());
            }
            reserveTicket.setTicketFrom(from.equals(1) ? "PC" : "租客");
            reserveTicketMapper.insert(reserveTicket);
            String nickName = "";
            if (from.equals(1)) {
                AdminUserRespDTO adminUserRespDTO = adminUserApi.getUser(userId).getCheckedData();
                if (ObjectUtil.isNotEmpty(adminUserRespDTO)) {
                    nickName = adminUserRespDTO.getNickname();
                }
            } else if (from.equals(2)) {
                List<MemberUserRespDTO> checkedData = memberUserApi.getUsersByIds(Arrays.asList(userId)).getCheckedData();
                if (ObjectUtil.isNotEmpty(checkedData)) {
                    nickName = checkedData.get(0).getNickname();
                }
            }
            log.info("创建预约工单请求 - loginUserInfo:{}", JsonUtils.toJsonString(nickName));

            // 添加记录
            TicketAssignRecordCreateReqVO createVO = new TicketAssignRecordCreateReqVO();
            createVO.setReserveTicketId(reserveTicket.getId());
            createVO.setOperatorsUserId(userId);
            createVO.setCustomerName(createReqVO.getCustomerName());
            // 备注信息
            StringBuilder sb = new StringBuilder();
            sb.append(nickName);
            sb.append("添加了客户");
            sb.append(createReqVO.getCustomerName());
            createVO.setRecord(sb.toString());

            // 短信通知对应门店
            MessageWhenCreateReserveTicketReqDTO createReserveTicketReqDTO = new MessageWhenCreateReserveTicketReqDTO()
                    .setMobile(apartmentListDto.getStoreTel());
            bpmMessageService.sendMessageWhenCreateReserveTicket(createReserveTicketReqDTO);
            log.info("[创建预约工单短信提醒], 工单ID:{}, 通知门店ID:{}, 门店名:{}", reserveTicket.getId(), apartmentListDto.getId(), apartmentListDto.getName());

            ticketAssignRecordService.createTicketAssignRecord(createVO);
            // 如果有指定跟进人, 保存指派记录
            if (ObjectUtil.isNotEmpty(createReqVO.getFollowUpUserId())) {
                createVO = new TicketAssignRecordCreateReqVO();
                createVO.setReserveTicketId(reserveTicket.getId());
                createVO.setOperatorsUserId(userId);
                createVO.setFollowUpUserId(createReqVO.getFollowUpUserId());
                createVO.setCustomerName(createReqVO.getCustomerName());

                AdminUserRespDTO assignUserInfo = adminUserApi.getUser(createReqVO.getFollowUpUserId()).getCheckedData();
                if (ObjectUtil.isEmpty(assignUserInfo)) {
                    throw new ServiceException(new ErrorCode(10021001, "用户不存在,id:" + createReqVO.getFollowUpUserId()));
                }
                sb = new StringBuilder();
                sb.append(nickName);
                sb.append("分配了客户");
                sb.append(createReqVO.getCustomerName());
                sb.append("给");
                sb.append(assignUserInfo.getNickname());
                createVO.setRecord(sb.toString());
                ticketAssignRecordService.createTicketAssignRecord(createVO);
                // 发送短信
                MessageWhenReserveTicketReqDTO reserveTicketReqDTO = new MessageWhenReserveTicketReqDTO()
                        .setUserId(createReqVO.getFollowUpUserId())
                        .setCustomerName(createReqVO.getCustomerName())
                        .setCustomerMobile(createReqVO.getMobile());
                bpmMessageService.sendMessageWhenReserveTicket(reserveTicketReqDTO);
            }
            // 返回
            return reserveTicket.getId();
        }
    }

    private Long countByReserveDateAndTime(Long apartmentId, LocalDate reserveDate, String reserveTimeStart, String reserveTimeEnd) {
        LambdaQueryWrapper<ReserveTicketDO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ReserveTicketDO::getApartmentId, apartmentId)
                .eq(ReserveTicketDO::getReserveDate, reserveDate)
                .eq(ReserveTicketDO::getReserveTimeStart, reserveTimeStart)
                .eq(ReserveTicketDO::getReserveTimeEnd, reserveTimeEnd);
        return reserveTicketMapper.selectCount(wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateReserveTicket(Long userId, ReserveTicketUpdateReqVO updateReqVO, HttpServletRequest request) {
        try(DistributeLock lock = DistributeLock.getLock(DistributeLockConstants.RESERVE_LOCK)) {
            if(lock.tryLock()){
                // ignoreDeptPermissions(request, SecurityFrameworkUtils.getLoginUser());
                // 校验存在
                validateReserveTicketExists(updateReqVO.getId());
                ApartmentListDto apartmentListDto = apartmentApi.getById(updateReqVO.getApartmentId()).getCheckedData();
                if (ObjectUtil.isEmpty(apartmentListDto)) {
                    throw new ServiceException(APARTMENT_NOT_EXIST);
                }
                // 集中式门店, 房型必选
                if (apartmentListDto.getIsConcentrated() && ObjectUtil.isEmpty(updateReqVO.getHouseType())) {
                    throw new ServiceException(HOUSETYPE_NOT_NULL);
                }
                // 集中式门店需要检查预约人数配置
                if(apartmentListDto.getIsConcentrated()){
                    checkReserveNum(updateReqVO.getApartmentId(), updateReqVO.getReserveDate(), updateReqVO.getReserveTimeStart(), updateReqVO.getReserveTimeEnd(), updateReqVO.getTimePeriod());
                }
                // 更新
                ReserveTicketDO updateObj = ReserveTicketConvert.INSTANCE.convert(updateReqVO);
                updateObj.setAddress(apartmentListDto.getCity() + apartmentListDto.getDistrict() + apartmentListDto.getStreet() + apartmentListDto.getAddress());
                // 如果跟进人发生变动
                ReserveTicketDO reserveTicketDO = reserveTicketMapper.selectById(updateReqVO.getId());
                AdminUserRespDTO loginUserInfo = adminUserApi.getUser(userId).getCheckedData();

                // 记录编辑了用户信息
                TicketAssignRecordCreateReqVO createVO = new TicketAssignRecordCreateReqVO();
                createVO.setReserveTicketId(updateReqVO.getId());
                createVO.setOperatorsUserId(userId);
                createVO.setFollowUpUserId(updateReqVO.getFollowUpUserId());
                createVO.setCustomerName(updateReqVO.getCustomerName());
                // 备注信息
                StringBuilder sb = new StringBuilder();
                sb.append(loginUserInfo.getNickname());
                sb.append("修改了客户信息");
                createVO.setRecord(sb.toString());
                ticketAssignRecordService.createTicketAssignRecord(createVO);

                if (ObjectUtil.isNotEmpty(updateReqVO.getFollowUpUserId()) && !updateReqVO.getFollowUpUserId().toString().equals(reserveTicketDO.getFollowUpUserId())) {
                    // 记录编辑了用户信息, 同时修改跟进人
                    createVO = new TicketAssignRecordCreateReqVO();
                    createVO.setReserveTicketId(updateReqVO.getId());
                    createVO.setOperatorsUserId(userId);
                    createVO.setFollowUpUserId(updateReqVO.getFollowUpUserId());
                    createVO.setCustomerName(updateReqVO.getCustomerName());

                    AdminUserRespDTO assignUserInfo = adminUserApi.getUser(updateReqVO.getFollowUpUserId()).getCheckedData();
                    if (ObjectUtil.isEmpty(assignUserInfo)) {
                        throw new ServiceException(new ErrorCode(10021001, "用户不存在,id:" + updateReqVO.getFollowUpUserId()));
                    }
                    sb = new StringBuilder();
                    sb.append(loginUserInfo.getNickname());
                    sb.append("分配了客户");
                    sb.append(updateReqVO.getCustomerName());
                    sb.append("给");
                    sb.append(assignUserInfo.getNickname());
                    createVO.setRecord(sb.toString());
                    ticketAssignRecordService.createTicketAssignRecord(createVO);
                }

                reserveTicketMapper.updateById(updateObj);
            }
        }
    }

    private void checkReserveNum(Long apartmentId, LocalDate reserveDate, String reserveTimeStart, String reserveTimeEnd, Integer timePeriod) {
        // 检查门店是否可预约
        ReserveApartmentConfig apartmentConfig = reserveApartmentConfigService.getByApartmentIdAndTimePeriod(apartmentId, timePeriod);
        if (ObjectUtil.isEmpty(apartmentConfig)) {
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(), "门店配置异常");
        }
        // 该时间段已经预约的数量
        Long reserveNum = this.countByReserveDateAndTime(apartmentId, reserveDate, reserveTimeStart, reserveTimeEnd);
        if(ObjectUtil.isNotEmpty(apartmentConfig.getNum()) && reserveNum >= apartmentConfig.getNum()){
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(), "该时间段已约满, 请重新选择预约时间");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteReserveTicket(Long id, HttpServletRequest request) {
        // ignoreDeptPermissions(request, SecurityFrameworkUtils.getLoginUser());
        // 校验存在
        validateReserveTicketExists(id);
        // 删除
        reserveTicketMapper.deleteById(id);
        // 删除关联操作记录
        ticketAssignRecordService.deleteByReserveTicketId(id);
    }

    private ReserveTicketDO validateReserveTicketExists(Long id) {
        ReserveTicketDO reserveTicketDO = reserveTicketMapper.selectById(id);
        if (reserveTicketDO == null) {
            throw exception(RESERVE_TICKET_NOT_EXISTS);
        }
        return reserveTicketDO;
    }

    @Override
    public GetReserveTicketRespVO getReserveTicket(Long id, HttpServletRequest request) {
        // 获取详情时不需要部门权限，分页查询能看到就说明有权限
        // ignoreDeptPermissions(request, SecurityFrameworkUtils.getLoginUser());

        ReserveTicketDO reserveTicketDO = reserveTicketMapper.selectById(id);
        ReserveTicketRespVO ticketRespVO = ReserveTicketConvert.INSTANCE.convert(reserveTicketDO);
        ticketRespVO.setReserveTimeStart(ObjectUtil.isNotEmpty(ticketRespVO.getReserveTimeStart()) ? ticketRespVO.getReserveTimeStart().substring(0, 5) : null);
        ticketRespVO.setReserveTimeEnd(ObjectUtil.isNotEmpty(ticketRespVO.getReserveTimeEnd()) ? ticketRespVO.getReserveTimeEnd().substring(0, 5) : null);
        // 查询分配记录
        List<TicketAssignRecordDO> ticketAssignRecordDOS = ticketAssignRecordService.getByTicketIdOrderById(ticketRespVO.getId());
        List<TicketAssignRecordRespVO> ticketAssignRecordRespVOS = TicketAssignRecordConvert.INSTANCE.convert(ticketAssignRecordDOS);

        GetReserveTicketRespVO respVO = new GetReserveTicketRespVO();
        respVO.setReserveTicketRespVO(ticketRespVO);
        respVO.setTicketAssignRecordRespVOS(ticketAssignRecordRespVOS);
        return respVO;
    }

    private static void ignoreDeptPermissions(HttpServletRequest request, LoginUser loginUser) {
        DeptDataPermissionRespDTO deptDataPermissionRespDTO = new DeptDataPermissionRespDTO();
        deptDataPermissionRespDTO.setAll(true);
        loginUser.setContext(DeptDataPermissionRule.class.getSimpleName(), deptDataPermissionRespDTO);
        SecurityFrameworkUtils.setLoginUser(loginUser, request);
    }

    @Override
    public List<ReserveTicketDO> getReserveTicketList(Collection<Long> ids) {
        return reserveTicketMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<ReserveTicketRespVO> getReserveTicketPage(ReserveTicketPageReqVO pageReqVO, HttpServletRequest request) {
        LambdaQueryWrapper<ReserveTicketDO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ObjectUtil.isNotEmpty(pageReqVO.getStatus()), ReserveTicketDO::getStatus, pageReqVO.getStatus());
        wrapper.eq(ObjectUtil.isNotEmpty(pageReqVO.getCustomerOrigin()), ReserveTicketDO::getCustomerOrigin, pageReqVO.getCustomerOrigin());
        wrapper.eq(ObjectUtil.isNotEmpty(pageReqVO.getUrgencyLevel()), ReserveTicketDO::getUrgencyLevel, pageReqVO.getUrgencyLevel());
        wrapper.eq(ObjectUtil.isNotEmpty(pageReqVO.getFollowUpUserId()), ReserveTicketDO::getFollowUpUserId, pageReqVO.getFollowUpUserId());
        if (ObjectUtil.isNotEmpty(pageReqVO.getProgress()) && ReserveProgressEnum.INVALID.getValue().equals(pageReqVO.getProgress())) {
            wrapper.in(ReserveTicketDO::getProgress, Arrays.asList(ReserveProgressEnum.ABANDON.getValue(), ReserveProgressEnum.INVALID.getValue()));
        } else {
            wrapper.eq(ObjectUtil.isNotEmpty(pageReqVO.getProgress()), ReserveTicketDO::getProgress, pageReqVO.getProgress());
        }
        wrapper.between(ObjectUtil.isNotEmpty(pageReqVO.getReserveDateStart()) && ObjectUtil.isNotEmpty(pageReqVO.getReserveDateEnd()), ReserveTicketDO::getReserveDate, pageReqVO.getReserveDateStart(), pageReqVO.getReserveDateEnd());
        wrapper.and(ObjectUtil.isNotEmpty(pageReqVO.getSearchBox()), w ->
                w.like(ReserveTicketDO::getCustomerName, pageReqVO.getSearchBox())
                        .or().like(ReserveTicketDO::getMobile, pageReqVO.getSearchBox())
                        .or().like(ReserveTicketDO::getAddress, pageReqVO.getSearchBox())
                        .or().like(ReserveTicketDO::getFollowUpUserId, pageReqVO.getSearchBox())
        );
        if (ObjectUtil.isNotEmpty(pageReqVO.getCreateTime())) {
            final LocalDate now = pageReqVO.getCreateTime();
            LocalDateTime start = now.atStartOfDay();
            LocalDateTime end = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), 23, 59, 59);
            wrapper.between(ReserveTicketDO::getCreateTime, start, end);
        }

        wrapper.orderByDesc(ReserveTicketDO::getUpdateTime);

        // 登录人能看到所有部门的待分配工单
//        if (ObjectUtil.isNotEmpty(pageReqVO.getStatus()) && ReserveStatusEnum.TO_BE_ASSIGNED.getValue().equals(pageReqVO.getStatus())) {
//            ignoreDeptPermissions(request, SecurityFrameworkUtils.getLoginUser());
//        }

        Page<ReserveTicketDO> page = reserveTicketMapper.selectPage(new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), wrapper);
        List<ReserveTicketRespVO> respVOS = ReserveTicketConvert.INSTANCE.convert(page.getRecords());
        respVOS.stream().forEach(reserveTicketRespVO -> {
            if (ObjectUtil.isNotEmpty(reserveTicketRespVO.getProgress())) {
                reserveTicketRespVO.setProgress(ReserveProgressEnum.getNameByValue(reserveTicketRespVO.getProgress()));
            }
            if (ObjectUtil.isNotEmpty(reserveTicketRespVO.getStatus())) {
                reserveTicketRespVO.setStatus(ReserveStatusEnum.getNameByValue(reserveTicketRespVO.getStatus()));
            }
            if (ObjectUtil.isNotEmpty(reserveTicketRespVO.getFollowUpUserId())) {
                AdminUserRespDTO checkedData = adminUserApi.getUser(reserveTicketRespVO.getFollowUpUserId()).getCheckedData();
                reserveTicketRespVO.setFollowUpUserName(ObjectUtil.isNotEmpty(checkedData) ? checkedData.getNickname() : null);
            }
            String reserveTimeStart = reserveTicketRespVO.getReserveTimeStart();
            String reserveTimeEnd = reserveTicketRespVO.getReserveTimeEnd();
            reserveTicketRespVO.setReserveTimeStart(StrUtil.isNotEmpty(reserveTimeStart) ? reserveTimeStart.substring(0, reserveTimeStart.length() - 3) : reserveTimeStart);
            reserveTicketRespVO.setReserveTimeEnd(StrUtil.isNotEmpty(reserveTimeEnd) ? reserveTimeEnd.substring(0, reserveTimeEnd.length() - 3) : reserveTimeEnd);
            // 查询最近工单的更新时间
            List<TicketAssignRecordDO> ticketAssignRecordDOS = ticketAssignRecordService.getByTicketIdOrderByCreateTime(reserveTicketRespVO.getId());
            if (ObjectUtil.isNotEmpty(ticketAssignRecordDOS)) {
                reserveTicketRespVO.setUpdateTime(ticketAssignRecordDOS.get(0).getCreateTime());
            }
        });
        return new PageResult<>(respVOS, page.getTotal());
    }

    /**
     * 预约工单导出
     *
     * @param exportReqVO 查询条件
     * @param request
     * @return
     */
    @Override
    public List<ReserveTicketRespVO> getReserveTicketList(ReserveTicketExportReqVO exportReqVO, HttpServletRequest request) {
        if (ObjectUtil.isEmpty(exportReqVO.getStatus())) {
            throw exception(RESERVE_TICKET_STATUS_NOT_EXISTS);
        }
        // 登录人能看到所有部门的待分配工单
//        if (ReserveStatusEnum.TO_BE_ASSIGNED.getValue().equals(exportReqVO.getStatus())) {
//            ignoreDeptPermissions(request, SecurityFrameworkUtils.getLoginUser());
//        }
        LambdaQueryWrapper<ReserveTicketDO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ReserveTicketDO::getStatus, exportReqVO.getStatus());
        wrapper.eq(ObjectUtil.isNotEmpty(exportReqVO.getCustomerOrigin()), ReserveTicketDO::getCustomerOrigin, exportReqVO.getCustomerOrigin());
        wrapper.eq(ObjectUtil.isNotEmpty(exportReqVO.getUrgencyLevel()), ReserveTicketDO::getUrgencyLevel, exportReqVO.getUrgencyLevel());
        wrapper.eq(ObjectUtil.isNotEmpty(exportReqVO.getProgress()), ReserveTicketDO::getProgress, exportReqVO.getProgress());
        wrapper.eq(ObjectUtil.isNotEmpty(exportReqVO.getFollowUpUserId()), ReserveTicketDO::getFollowUpUserId, exportReqVO.getFollowUpUserId());
        wrapper.between(ObjectUtil.isNotEmpty(exportReqVO.getReserveDateStart()) && ObjectUtil.isNotEmpty(exportReqVO.getReserveDateEnd()), ReserveTicketDO::getReserveDate, exportReqVO.getReserveDateStart(), exportReqVO.getReserveDateEnd());
        wrapper.and(ObjectUtil.isNotEmpty(exportReqVO.getSearchBox()), w ->
                w.like(ReserveTicketDO::getCustomerName, exportReqVO.getSearchBox())
                        .or().like(ReserveTicketDO::getMobile, exportReqVO.getSearchBox())
                        .or().like(ReserveTicketDO::getAddress, exportReqVO.getSearchBox())
                        .or().like(ReserveTicketDO::getFollowUpUserId, exportReqVO.getSearchBox())
        );
        if(ObjectUtil.isNotEmpty(exportReqVO.getCreateTime())){
            final LocalDate now = exportReqVO.getCreateTime();
            LocalDateTime start = now.atStartOfDay();
            LocalDateTime end = LocalDateTime.of(now .getYear(), now .getMonth(), now .getDayOfMonth(), 23, 59, 59);
            wrapper.between(ReserveTicketDO::getCreateTime, start, end);
        }
        wrapper.orderByDesc(ReserveTicketDO::getUpdateTime);
        List<ReserveTicketDO> reserveTicketDOS = reserveTicketMapper.selectList(wrapper);
        List<ReserveTicketRespVO> respVOS = ReserveTicketConvert.INSTANCE.convert(reserveTicketDOS);

        respVOS.stream().forEach(reserveTicketRespVO -> {
            if (ObjectUtil.isNotEmpty(reserveTicketRespVO.getFollowUpUserId())) {
                AdminUserRespDTO checkedData = adminUserApi.getUser(reserveTicketRespVO.getFollowUpUserId()).getCheckedData();
                reserveTicketRespVO.setFollowUpUserName(ObjectUtil.isNotEmpty(checkedData) ? checkedData.getNickname() : null);
            }
        });
        return respVOS;
    }

    /**
     * 预约工单 - 分配跟进人
     *
     * @param loginUser
     * @param assignmentReqVO
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long assignment(LoginUser loginUser, AssignmentReqVO assignmentReqVO, HttpServletRequest request) {
        // 指派跟进人时不需要部门权限，分页查询能看到就说明有权限
        // ignoreDeptPermissions(request, loginUser);

        // 修改跟进人
        ReserveTicketDO reserveTicketDO = reserveTicketMapper.selectById(assignmentReqVO.getReserveTicketId());
        if (ObjectUtil.isEmpty(reserveTicketDO)) {
            throw exception(RESERVE_TICKET_NOT_EXISTS);
        }

        AdminUserRespDTO adminUserRespDTO = adminUserApi.getUser(assignmentReqVO.getFollowUpUserId()).getCheckedData();
        reserveTicketDO.setStatus(ReserveStatusEnum.FOLLOW_UP.getValue());
        reserveTicketDO.setFollowUpUserId(assignmentReqVO.getFollowUpUserId());
        reserveTicketDO.setDeptId(adminUserRespDTO.getDeptId());
        reserveTicketMapper.updateById(reserveTicketDO);
        // 记录编辑了用户信息
        TicketAssignRecordCreateReqVO createVO = new TicketAssignRecordCreateReqVO();
        createVO.setReserveTicketId(assignmentReqVO.getReserveTicketId());
        createVO.setOperatorsUserId(loginUser.getId());
        createVO.setFollowUpUserId(assignmentReqVO.getFollowUpUserId());
        createVO.setCustomerName(assignmentReqVO.getCustomerName());

        AdminUserRespDTO assignUserInfo = adminUserApi.getUser(assignmentReqVO.getFollowUpUserId()).getCheckedData();
        if (ObjectUtil.isEmpty(assignUserInfo)) {
            throw new ServiceException(new ErrorCode(10021001, "用户不存在,id:" + assignmentReqVO.getFollowUpUserId()));
        }

        StringBuilder sb = new StringBuilder();
        sb.append(loginUser.getNickname());
        sb.append("分配了客户");
        sb.append(assignmentReqVO.getCustomerName());
        sb.append("给");
        sb.append(assignUserInfo.getNickname());
        createVO.setRecord(sb.toString());
        Long assignRecordId = ticketAssignRecordService.createTicketAssignRecord(createVO);
        // 发送短信
        MessageWhenReserveTicketReqDTO reserveTicketReqDTO = new MessageWhenReserveTicketReqDTO()
                .setUserId(assignmentReqVO.getFollowUpUserId())
                .setCustomerName(assignmentReqVO.getCustomerName())
                .setCustomerMobile(assignmentReqVO.getCustomerMobile());
        bpmMessageService.sendMessageWhenReserveTicket(reserveTicketReqDTO);
        log.info("预约工单发送短信给处理人,处理人id:{}", assignmentReqVO.getFollowUpUserId());
        return assignRecordId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean modifyProgress(Long userId, ModifyProgressReqVO modifyProgressReqVO) {
        // 修改进度
        ReserveTicketDO reserveTicketDO = reserveTicketMapper.selectById(modifyProgressReqVO.getReserveTicketId());
        if (ObjectUtil.isEmpty(reserveTicketDO)) {
            throw exception(RESERVE_TICKET_NOT_EXISTS);
        }
        reserveTicketDO.setRemark(modifyProgressReqVO.getRemark());
        reserveTicketDO.setProgress(modifyProgressReqVO.getProgress());
        // 如果是放弃和无效, 状态置为已完成
        if (ObjectUtil.contains(Arrays.asList(ReserveProgressEnum.ABANDON.getValue(), ReserveProgressEnum.INVALID.getValue(), ReserveProgressEnum.CONTRACTED.getValue()), modifyProgressReqVO.getProgress())) {
            reserveTicketDO.setStatus(ReserveStatusEnum.COMPLETED.getValue());
        }
        reserveTicketMapper.updateById(reserveTicketDO);
        // 添加记录
        TicketAssignRecordCreateReqVO createVO = new TicketAssignRecordCreateReqVO();
        createVO.setReserveTicketId(modifyProgressReqVO.getReserveTicketId());
        createVO.setOperatorsUserId(userId);
        createVO.setFollowUpUserId(reserveTicketDO.getFollowUpUserId());
        createVO.setCustomerName(modifyProgressReqVO.getCustomerName());

        AdminUserRespDTO loginUserInfo = adminUserApi.getUser(userId).getCheckedData();

        StringBuilder sb = new StringBuilder();
        sb.append(loginUserInfo.getNickname());
        sb.append("标记客户为: ");
        sb.append(ReserveProgressEnum.getNameByValue(modifyProgressReqVO.getProgress()));
        createVO.setRecord(sb.toString());
        ticketAssignRecordService.createTicketAssignRecord(createVO);
        return Boolean.TRUE;
    }

    /**
     * 获取进度枚举 - 排除待处理,已签约
     *
     * @return
     */
    @Override
    public List<GetProgressEnumRespVO> getProgressEnum() {
        List<GetProgressEnumRespVO> respVOS = new ArrayList<>();
        Arrays.stream(ReserveProgressEnum.values()).filter(p -> {
            if (ObjectUtil.contains(Arrays.asList(ReserveProgressEnum.PENDING.getValue()), p.getValue())) {
                return false;
            }
            return true;
        }).sorted(Comparator.comparing(ReserveProgressEnum::getValue)).forEach(p -> {
            respVOS.add(new GetProgressEnumRespVO().setName(p.getName()).setValue(p.getValue()));
        });
        return respVOS;
    }

    /**
     * 分页状态数量统计
     *
     * @return
     */
    @Override
    public StatusCountRespVO statusCount(Integer status, HttpServletRequest request) {
        // 登录人能看到所有部门的待分配工单
//        if (ReserveStatusEnum.TO_BE_ASSIGNED.getValue().equals(String.valueOf(status))) {
//            ignoreDeptPermissions(request, SecurityFrameworkUtils.getLoginUser());
//        }
        StatusCountRespVO respVO = new StatusCountRespVO();
        // 按紧急程度统计
        // 紧急
        respVO.setUrgent(this.countByUrgencyLevel(UrgencyLevelEnum.URGENT.getValue(), status));
        // 正常
        respVO.setNormal(this.countByUrgencyLevel(UrgencyLevelEnum.NORMAL.getValue(), status));
        // 延后
        respVO.setDelayed(this.countByUrgencyLevel(UrgencyLevelEnum.DELAYED.getValue(), status));
        // 当前状态下的数量
        respVO.setCurrentStatusNum(this.countByProgress(null, status));
        // 已去电
        respVO.setCalled(this.countByProgress(Arrays.asList(ReserveProgressEnum.CALLED.getValue()), status));
        // 待处理
        respVO.setPending(this.countByProgress(Arrays.asList(ReserveProgressEnum.PENDING.getValue()), status));
        // 已带看
        respVO.setTakenToSee(this.countByProgress(Arrays.asList(ReserveProgressEnum.TAKEN_TO_SEE.getValue()), status));
        // 推迟
        respVO.setPostpone(this.countByProgress(Arrays.asList(ReserveProgressEnum.POSTPONE.getValue()), status));
        // 已签约
        respVO.setContracted(this.countByProgress(Arrays.asList(ReserveProgressEnum.CONTRACTED.getValue()), status));
        // 无效
        respVO.setInvalid(this.countByProgress(Arrays.asList(ReserveProgressEnum.INVALID.getValue(), ReserveProgressEnum.ABANDON.getValue()), status));
        return respVO;
    }

    /**
     * 个人中心-预约工单列表
     *
     * @param mobile
     * @param pageSize
     * @param pageNo
     * @return
     */
    @Override
    public PageResult<PersonalReserveRespVO> personalReservePage(String mobile, Integer pageSize, Integer pageNo) {
        // 租客手机号查询预约工单列表
        LambdaQueryWrapper<ReserveTicketDO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ReserveTicketDO::getMobile, mobile);
        Page<ReserveTicketDO> page = reserveTicketMapper.selectPage(new Page<>(pageNo, pageSize), wrapper);

        List<PersonalReserveRespVO> respVOS = page.getRecords().stream().map(reserveTicketDO -> {
            PersonalReserveRespVO personalReserveRespVO = new PersonalReserveRespVO();
            BeanUtil.copyProperties(reserveTicketDO, personalReserveRespVO);
            List<TicketAssignRecordDO> ticketAssignRecordDOS = ticketAssignRecordService.getByTicketIdOrderByCreateTime(reserveTicketDO.getId());
            if (ObjectUtil.isNotEmpty(ticketAssignRecordDOS)) {
                personalReserveRespVO.setFollowUpTime(ticketAssignRecordDOS.get(0).getCreateTime());
            }
            return personalReserveRespVO;
        }).collect(Collectors.toList());
        return new PageResult<>(respVOS, page.getTotal());
    }

    /**
     * 租客端创建预约工单
     *
     * @param userId
     * @param createReqVO
     * @return
     */
    @Override
    public Long tenantReserveTicket(Long userId, ReserveCreateReqVO createReqVO) {
        // 检查客户是否重复预约
        LambdaQueryWrapper<ReserveTicketDO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ReserveTicketDO::getMobile, createReqVO.getMobile());
        wrapper.eq(ReserveTicketDO::getApartmentId, createReqVO.getApartmentId());
        wrapper.in(ReserveTicketDO::getStatus, CollectionUtil.toList(ReserveStatusEnum.TO_BE_ASSIGNED.getValue(), ReserveStatusEnum.FOLLOW_UP.getValue()));
        List<ReserveTicketDO> reserveTicketDOS = reserveTicketMapper.selectList(wrapper);
        if (CollectionUtil.isNotEmpty(reserveTicketDOS)) {
            throw new ServiceException(RESERVE_TICKET_REPEAT);
        }
        ApartmentListDto apartmentListDto = apartmentApi.getById(createReqVO.getApartmentId()).getCheckedData();
        ReserveTicketCreateReqVO ticketCreateReqVO = new ReserveTicketCreateReqVO();
        BeanUtil.copyProperties(createReqVO, ticketCreateReqVO);
        ticketCreateReqVO.setCustomerType(CustomerTypeEnum.PERSONAL.getValue());
        ticketCreateReqVO.setUrgencyLevel(UrgencyLevelEnum.NORMAL.getValue());
        ticketCreateReqVO.setApartmentName(apartmentListDto.getName());
        return this.createReserveTicket(userId, ticketCreateReqVO, 2);
    }

    /**
     * 租客端 - 我的预约
     *
     * @param mobile
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public PageResult<MyReserveTicketPageRespVO> myReserveTicketPage(@NonNull String mobile, Integer pageNo, Integer pageSize) {
        LambdaQueryWrapper<ReserveTicketDO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ReserveTicketDO::getMobile, mobile);
        wrapper.orderByDesc(ReserveTicketDO::getCreateTime);
        Page<ReserveTicketDO> page = reserveTicketMapper.selectPage(new Page<>(pageNo, pageSize), wrapper);
        List<MyReserveTicketPageRespVO> respVOS = page.getRecords().stream().map(p -> {
            MyReserveTicketPageRespVO myReserveTicketPageRespVO = new MyReserveTicketPageRespVO();
            BeanUtil.copyProperties(p, myReserveTicketPageRespVO);
            myReserveTicketPageRespVO.setIsTakingOrders(ObjectUtil.isNotEmpty(p.getFollowUpUserId()));
            return myReserveTicketPageRespVO;
        }).collect(Collectors.toList());
        return new PageResult<>(respVOS, page.getTotal());
    }

    /**
     * 统计待完成预约工单总数
     *
     * @return
     */
    @Override
    public GetTotalNumberRespVO getTotalNumber(Long userId) {
        // 统计待完成预约工单总数, 排除已完成
        LambdaQueryWrapper<ReserveTicketDO> wapper = Wrappers.lambdaQuery();
        wapper.in(ReserveTicketDO::getStatus, Arrays.asList(ReserveStatusEnum.TO_BE_ASSIGNED.getValue(), ReserveStatusEnum.FOLLOW_UP.getValue()));
        Long reserveTicketCount = reserveTicketMapper.selectCount(wapper);
        // 统计未完成的服务工单
        Long swoTicketCount = swoTicketService.getTotalNumber();
        // 统计未完成的委托数量
        Long entrustCount = roomEntrustApi.toBeContinuedEntrustNum().getCheckedData();
        // 未完成的审批数量
        Long approvalCount = bpmTaskService.getTodoTaskCount(userId);
        // 未完成的租客申请数量
        Long contractApplyCount = contractApplyApi.contractApplyNum().getCheckedData();
        //未处理数量
        Long noChangeLogCount = financePaymentChangeLogApi.noChangeLogCount().getCheckedData();
        // 未完成的意见反馈数量
        Long feedbackCount = feedbackApi.incompleteNum();
        // 待办备忘录数量
        Long memoCount = bpmMemoService.getMemoCount(userId);
        return new GetTotalNumberRespVO().
                setReserveTicketCount(reserveTicketCount).
                setSwoTicketCount(swoTicketCount)
                .setEntrustCount(entrustCount)
                .setApprovalCount(approvalCount)
                .setContractApplyCount(contractApplyCount)
                .setPaymentChangeLogCount(noChangeLogCount)
                .setMemoCount(memoCount)
                .setFeedbackCount(feedbackCount);

    }

    /**
     * 管家端 - 预约看房 和 紧急报修数量
     *
     * @return
     */
    @Override
    public ReserveUrgentRepariNumRespVO reserveUrgentRepariNum() {
        // 预约看房的数量
        LambdaQueryWrapper<ReserveTicketDO> wrapper = Wrappers.lambdaQuery();
        // 过滤掉已完成的
        wrapper.ne(ReserveTicketDO::getStatus, ReserveStatusEnum.COMPLETED.getValue());
        Long reserveNum = reserveTicketMapper.selectCount(wrapper);
        // 紧急保修的工单数量
        Long urgentRepariNum = swoTicketService.urgentRepariNum();
        return new ReserveUrgentRepariNumRespVO().setReserveNum(reserveNum).setUrgentRepariNum(urgentRepariNum);
    }

    /**
     * 取消预约工单
     *
     * @param reserveTicketId 预约工单id
     * @param mobile
     * @return
     */
    @Override
    public Boolean cancelReserveTicket(Long reserveTicketId, String mobile) {
        // 检查预约工单是否是当前登陆人的
        ReserveTicketDO reserveTicketDO = validateReserveTicketExists(reserveTicketId);
        if(!mobile.equals(reserveTicketDO.getMobile())){
            throw new ServiceException(CANCEL_RESERVE_TICKET_ERROR);
        }
        // 如果该预约没有被接单则可以取消
        if(ObjectUtil.isNotEmpty(reserveTicketDO.getFollowUpUserId())){
            throw new ServiceException(RECEIVED_ORDER_CANCEL_RESERVE_TICKET_ERROR);
        }
        // 预约工单改为已完成， 进度为放弃
        reserveTicketDO.setStatus(ReserveStatusEnum.COMPLETED.getValue());
        reserveTicketDO.setProgress(ReserveProgressEnum.ABANDON.getValue());
        reserveTicketDO.setRemark("用户主动取消预约");
        reserveTicketMapper.updateById(reserveTicketDO);
        return Boolean.TRUE;
    }

    /**
     * 获取各时间段下可租数量
     *
     * @param reqVO
     * @param apartmentId
     * @param reserveDate
     * @return
     */
    @Override
    public Map<Integer, Integer> remainingReserveQuantity(@NonNull Long apartmentId,@NonNull LocalDate reserveDate) {
        LambdaQueryWrapper<ReserveTicketDO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ReserveTicketDO::getApartmentId, apartmentId);
        wrapper.eq(ReserveTicketDO::getReserveDate, reserveDate);
        // 指定日期的预约记录
        List<ReserveTicketDO> reserveTicketDOS = reserveTicketMapper.selectList(wrapper);
        // 按预约时间段分组
        Map<Integer, List<ReserveTicketDO>> map = reserveTicketDOS.stream().filter(p -> ObjectUtil.isNotEmpty(p.getReserveTimeIndex())).collect(Collectors.groupingBy(ReserveTicketDO::getReserveTimeIndex));
        // 获取门店预约人数配置信息
        List<ReserveApartmentConfig> reserveApartmentConfigs = reserveApartmentConfigService.getByApartmentId(apartmentId);
        Map<Integer, Integer> reqMap = new HashMap<>();
        for (ReserveApartmentConfig reserveApartmentConfig : reserveApartmentConfigs) {
            if(ObjectUtil.isEmpty(reserveApartmentConfig.getNum())){
                reqMap.put(reserveApartmentConfig.getTimePeriod(), reserveApartmentConfig.getNum());
                continue;
            }
            if(map.containsKey(reserveApartmentConfig.getTimePeriod())) {
                List<ReserveTicketDO> reserveTicketDOS1 = map.get(reserveApartmentConfig.getTimePeriod());
                if(ObjectUtil.isEmpty(reserveTicketDOS1)){
                    reqMap.put(reserveApartmentConfig.getTimePeriod(), reserveApartmentConfig.getNum());
                }else {
                    int remainingQuantity = reserveApartmentConfig.getNum() - reserveTicketDOS1.size();
                    reqMap.put(reserveApartmentConfig.getTimePeriod(), Math.max(remainingQuantity, 0));
                }
            }else{
                reqMap.put(reserveApartmentConfig.getTimePeriod(), reserveApartmentConfig.getNum());
            }
        }
        return reqMap;
    }

    private Long countByProgress(List<String> progress, Integer status) {
        LambdaQueryWrapper<ReserveTicketDO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ReserveTicketDO::getStatus, status);
        wrapper.in(ObjectUtil.isNotEmpty(progress), ReserveTicketDO::getProgress, progress);
        return reserveTicketMapper.selectCount(wrapper);
    }

    private Long countByUrgencyLevel(String urgencyLevel, Integer status) {
        LambdaQueryWrapper<ReserveTicketDO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ReserveTicketDO::getStatus, status);
        wrapper.eq(ReserveTicketDO::getUrgencyLevel, urgencyLevel);
        return reserveTicketMapper.selectCount(wrapper);
    }
}
